package 中国.珍林.AI.图片.分类.Servlet;

import jakarta.annotation.Resource;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import 中国.易.工具.Servlet工具;
import 中国.珍林.AI.图片.分类.Clip客户端;
import 中国.珍林.AI.图片.分类.对象.关系_图片_标签;
import 中国.珍林.AI.图片.分类.对象.图片;
import 中国.珍林.AI.图片.分类.对象.标签;
import 中国.珍林.AI.图片.分类.服务.接口.I关系_图片_标签服务;
import 中国.珍林.AI.图片.分类.服务.接口.I图片服务;
import 中国.珍林.AI.图片.分类.服务.接口.I标签服务;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

@Log4j2
@WebServlet(urlPatterns = "/对图片进行打标")
public class 对图片进行打标 extends HttpServlet
{
    @Resource
    private I图片服务 图片服务;

    @Resource
    private I标签服务 标签服务;

    @Resource
    private I关系_图片_标签服务 关系_图片_标签服务;

    Clip客户端 c = new Clip客户端();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
    {
        List<标签> bqlist = 标签服务.查询所有();
        List<封装> 封装List = new ArrayList<>(bqlist.size());
        for (标签 bq : bqlist)
        {
            封装 fz = new 封装();
            封装List.add(fz);
            fz.标签 = bq;
            fz.vector = new ArrayRealVector(c.文本转向量(bq.get提示语()));
        }

        log.info("完成标签向量化");

        List<图片> list = 图片服务.查询所有();
        for (图片 tp : list)
        {
            if (!tp.is已处理())
            {
                匹配(封装List, FileUtils.readFileToByteArray(new File(tp.get本地地址())), tp.get文件名());

                int i = 0;
                for (封装 fz : 封装List)
                {
                    if (fz.最高分 > fz.标签.get阈值())
                    {
                        关系_图片_标签 gx = new 关系_图片_标签();
                        gx.set标签(fz.标签);
                        gx.set图片(tp);
                        gx.set分数(fz.最高分);
                        关系_图片_标签服务.添加(gx);
                        i++;
                    }
                    fz.最高分 = 0;
                }

                tp.set已处理(true);
                图片服务.修改(tp);

                log.info("完成图片{}的打标，共{}", tp.getId(), i);
            }
        }

        Servlet工具.返回内容(resp, "ok", null);
    }

    public void 匹配(List<封装> 封装List, byte[] image, String 文件名)
    {
        Callable<Boolean> callable = () -> 执行匹配(封装List, image, 文件名);
        FutureTask<Boolean> 匹配任务 = new FutureTask<>(callable);
        new Thread(匹配任务).start();

        切分成9张图并匹配(封装List, image, 文件名);

        try
        {
            if (!匹配任务.get())
            {
                log.warn("匹配失败");
            }
        }
        catch (InterruptedException | ExecutionException e)
        {
            throw new RuntimeException(e);
        }
    }

    private void 切分成9张图并匹配(List<封装> 封装List, byte[] image, String 文件名)
    {
        Mat mat = Imgcodecs.imdecode(new MatOfByte(image), Imgcodecs.IMREAD_UNCHANGED);

        int width = (int) mat.size().width;
        int height = (int) mat.size().height;
        int cw = width / 14;
        int ch = height / 14;

        List<FutureTask<Boolean>> rws = new ArrayList<>();
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 4, ch * 4, cw * 6, ch * 6);   // 5

        切分照片并匹配(封装List, mat, 文件名, rws, 0, ch * 4, cw * 5, ch * 6);        // 4
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 9, ch * 4, cw * 5, ch * 6);   // 6
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 4, 0, cw * 6, ch * 5);        // 8
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 4, ch * 9, cw * 6, ch * 5);   // 2

        切分照片并匹配(封装List, mat, 文件名, rws, 0, 0, cw * 5, ch * 5);             // 7
        切分照片并匹配(封装List, mat, 文件名, rws, 0, ch * 9, cw * 5, ch * 5);        // 1
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 9, 0, cw * 5, ch * 5);        // 9
        切分照片并匹配(封装List, mat, 文件名, rws, cw * 9, ch * 9, cw * 5, ch * 5);   // 3

        int x = 0;
        for (FutureTask<Boolean> rw : rws)
        {
            try
            {
                if (!rw.get())
                {
                    x++;
                }
            }
            catch (InterruptedException | ExecutionException e)
            {
                throw new RuntimeException(e);
            }
        }
        if (x != 0)
        {
            log.error("拆分匹配任务失败{}次", x);
        }
    }

    private void 切分照片并匹配(List<封装> 封装List, Mat mat, String 文件名, List<FutureTask<Boolean>> rws, int x,
            int y, int w, int h)
    {
        Rect rect = new Rect(x, y, w, h);
        Mat chunk = new Mat(mat, rect);

        MatOfByte m = new MatOfByte();
        Imgcodecs.imencode(文件名.substring(文件名.lastIndexOf(".")), chunk, m);

        Callable<Boolean> callable = () -> 执行匹配(封装List, m.toArray(), 文件名);
        FutureTask<Boolean> 匹配任务 = new FutureTask<>(callable);
        rws.add(匹配任务);
        new Thread(匹配任务).start();
    }

    private Boolean 执行匹配(List<封装> 封装List, byte[] image, String 文件名)
    {
        log.info("开始匹配食材");
        double[] d = c.图片转向量(image, 文件名);
        ArrayRealVector 目标 = new ArrayRealVector(d);

        for (封装 fz : 封装List)
        {
            double 相似度 = 目标.cosine(fz.vector);
            if (相似度 > fz.最高分)
            {
                fz.最高分 = 相似度;
            }
        }
        return true;
    }

    public static class 封装
    {
        标签 标签;
        ArrayRealVector vector;
        double 最高分 = 0;
    }
}